﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;

using ORM;
using static Common;
using static Constants;
using static DataLogic;

/// <summary>
/// HandlerBase 的摘要说明
/// </summary>
public class HandlerBase : IHttpHandler
{
	protected DbOperation DbOp = new SqlOperation(ConnStr);
	protected HttpRequest Request = HttpContext.Current.Request;
	protected HttpResponse Response = HttpContext.Current.Response;

	public virtual bool IsReusable => false;

	public virtual void ProcessRequest(HttpContext context)
	{
		if (context.IsWebSocketRequest)
			try
			{
				context.AcceptWebSocketRequest(websocket);
			}
			catch (Exception ex)
			{
				var id = Convert.ToUInt64(Helper.NewId).Convert(62, out var _);
				LogError(id, ex);
			}
		else
			try
			{
				http();
			}
			catch (Exception ex)
			{
				DbOp.Rollback();
				DbOp.Connection.Close();

				var id = Convert.ToUInt64(Helper.NewId).Convert(62, out var _);
				ex = ex.InnerException ?? ex;

				if (ex is Error)
				{
					if ((ex as Error).Plain)
						Response.Write(ex.Message);
					else if ((ex as Error).Record)
						Reply(new { err = $"〖{id}〗{ex.Message}" });
					else
						Reply(new { err = ex.Message });
				}
				else
				{
					LogError(id, ex);
					Reply(new { err = $"：〖{id}〗发生未知错误：{ex.Message}" });
				}
			}
	}

	/// <summary>
	/// ws通信
	/// </summary>
	/// <param name="arg"></param>
	/// <returns></returns>
	async Task websocket(AspNetWebSocketContext arg)
	{
		var socket = arg.WebSocket;

		bool exit = true;
		AdvancedLock.EnterMutexBlock(() =>
		{
			WebSockets.Add(socket);
			exit = false;
		});

		if (exit) return;

		var buffer = new ArraySegment<byte>(new byte[10000]);
		while (socket.State == WebSocketState.Open)
		{
			var result = await socket.ReceiveAsync(buffer, CancellationToken.None);

			if (result.MessageType == WebSocketMessageType.Close)       //无论主动close还是直接关闭浏览器或杀死浏览器进程都可以进入此处
			{
				await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
				AdvancedLock.EnterMutexBlock(() => WebSockets.Remove(socket));
			}
		}
	}

	/// <summary>
	/// http通信
	/// </summary>
	void http()
	{
		Response.ContentType = "text/plain";

		var cmd = Request["cmd"];
		if (string.IsNullOrEmpty(cmd)) throw new Error("必须提供cmd参数");

		var module = Request.Url.AbsolutePath;

		var method = GetType().GetMethod(cmd);
		if (method == null) throw new Error("逻辑接口不存在");

		var param = new StreamReader(Request.InputStream).ReadToEnd();
		Dictionary<string, object> parameters = null;
		try { parameters = JsonSerializer.Deserialize<Dictionary<string, object>>(param); } catch { }

		var args = new List<object>();

		foreach (var arg in method.GetParameters().OrderBy(a => a.Position))
		{
			var has = Request.QueryString.AllKeys.Contains(arg.Name) || Request.Form.AllKeys.Contains(arg.Name) || Request.Files.AllKeys.Contains(arg.Name) || parameters?.ContainsKey(arg.Name) == true;
			if (!has)
			{
				if (arg.HasDefaultValue)
				{
					args.Add(arg.DefaultValue);
					continue;
				}
				else
				{
					Reply(new { err = $"{cmd}缺少必要参数{arg.Name}" });
					return;
				}
			}

			var val = Request.QueryString[arg.Name] ?? Request.Form[arg.Name] ?? parameters?[arg.Name]?.ToString();
			var type = arg.ParameterType;
			switch (type.Name)
			{
				case "Byte":
					byte.TryParse(val, out byte bt);
					args.Add(bt);
					break;

				case "Int32":
					int.TryParse(val, out int it);
					args.Add(it);
					break;

				case "Int64":
					long.TryParse(val, out long lg);
					args.Add(lg);
					break;

				case "Decimal":
					decimal.TryParse(val, out decimal dc);
					args.Add(dc);
					break;

				case "Boolean":
					bool.TryParse(val, out bool bl);
					args.Add(bl);
					break;

				case "DateTime":
					DateTime.TryParse(val, out DateTime dt);
					args.Add(dt);
					break;

				case "String":
					args.Add(val);
					break;

				case "HttpPostedFile":
					args.Add(Request.Files[arg.Name]);
					break;

				default:        //针对DataObject类型的参数，则查出其id对应的记录
					if (type.IsSubclassOf(typeof(DataObject)))
					{
						long.TryParse(val, out long id);
						var obj = type.Assembly.CreateInstance(type.FullName, false, BindingFlags.Default, null, new[] { DbOp }, null, null) as DataObject;
						args.Add(obj.GetById(id));
					}
					break;
			}
		}

		validatePrivilege(method);
		var result = method.Invoke(this, args.ToArray());
		if (result == null) result = new { ok = true }; else result = new { ok = true, data = result };
		DbOp.Commit();
		DbOp.Connection.Close();
		Reply(result);
	}

	protected void Reply(dynamic result) => HttpContext.Current.Response.Write(JsonSerializer.Serialize(result));

	/// <summary>
	/// 校验访问权限
	/// </summary>
	/// <param name="method"></param>
	void validatePrivilege(MethodInfo method)
	{
		if (method.GetCustomAttribute<NoLogin>() == null && GetCurrentUser(DbOp) == null) throw new Error("未登录，请先登录") { Record = false };
	}
}

public class Error : Exception
{
	public bool Plain = false, Record = true;
	public Error(string msg = "") : base(msg) { }
}